package top.lywivan.core.registrar;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import top.lywivan.core.annotation.EnableLywScan;
import top.lywivan.core.annotation.Lyw;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @desc
 * @author:ivan.liu
 */
public class MyBeanRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //通过Spring的Scanner获取某一包下具有一定特征的类
        ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
        //创建过滤器，确定类的特征
        AnnotationTypeFilter filter = new AnnotationTypeFilter(Lyw.class);
        //设置Scanner的过滤方式
        scanner.addIncludeFilter(filter);
        //从注解中获取要扫描的包
        Set<String> basePackages = this.getBasePackages(annotationMetadata);
        //遍历所有的包路径
        for (String basePackage : basePackages) {
            //拿到有@Lyw注解的类
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            //对候选的Bean进行封装，生成对应的代理
            for (BeanDefinition candidateComponent : candidateComponents) {
                //强转为含注解的BeanDefinition
                if(candidateComponent instanceof AnnotatedBeanDefinition){
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    //获取所有注解中的信息
                    AnnotationMetadata lywAnnotationMetadata = beanDefinition.getMetadata();
                    //判断加@Lyw注解的是否为接口
                    Assert.isTrue(lywAnnotationMetadata.isInterface(),"@Lyw注解只能用在接口上！");
                    //获取@Lyw注解的属性
                    Map<String, Object> attributes = lywAnnotationMetadata.getAnnotationAttributes(Lyw.class.getCanonicalName());
                    //向容器中注册Bean
                    registerLywBean(beanDefinitionRegistry,lywAnnotationMetadata,attributes);
                }
            }
        }
    }

    private void registerLywBean(BeanDefinitionRegistry beanDefinitionRegistry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        //获取接口全路径
        String className = annotationMetadata.getClassName();
        //基于FactoryBean进行动态代理
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyLywFactoryBean.class);
        //设置相关属性
        builder.addPropertyValue("value",attributes.get("value"));
        builder.addPropertyValue("name",attributes.get("name"));
        builder.addPropertyValue("password",attributes.get("password"));
        builder.addPropertyValue("type",className);
        //设置注入类型
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        //获取最终的Bean对象
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        beanDefinition.setPrimary(true);
        //使用Holder包装
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,new String[]{"lywApi"});
        //向容器中注册Bean定义信息
        BeanDefinitionReaderUtils.registerBeanDefinition(holder,beanDefinitionRegistry);
    }

    private Set<String> getBasePackages(AnnotationMetadata annotationMetadata) {
        Map<String, Object> attributes = annotationMetadata
                .getAnnotationAttributes(EnableLywScan.class.getCanonicalName());

        Set<String> basePackages = new HashSet<>();
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        if (basePackages.isEmpty()) {
            basePackages.add(
                    ClassUtils.getPackageName(annotationMetadata.getClassName()));
        }
        return basePackages;
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }
}
